#pragma once
#include <vector>
#include <functional>
#include <sys/epoll.h>
#include "tcp_socket.hpp"

typedef std::function<void(const std::string &, std::string *resp)> Handler;

// 指关心读就绪
class Epoll
{
private:
    int epoll_fd;

public:
    Epoll()
    {
        // 创建一个epoll句柄
        epoll_fd = epoll_create(10);
    }
    ~Epoll()
    {
        close(epoll_fd);
    }
    bool Add(const TcpSocket &sock) const
    {
        int fd = sock.GetFd();
        printf("Epoll::Add : %d\n", fd);
        epoll_event ev;
        ev.data.fd = fd;
        ev.events = EPOLLIN;
        int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
        if (ret < 0)
        {
            perror("epoll_ctl add");
            return false;
        }
        return true;
    }
    bool Del(const TcpSocket &sock) const
    {
        int fd = sock.GetFd();
        printf("Epoll::Del: %d\n", fd);
        int ret = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
        if (ret < 0)
        {
            perror("epoll_ctl del");
            return false;
        }
        return true;
    }
    bool Wait(std::vector<TcpSocket> *output) const
    {
        output->clear();
        epoll_event events[1000];
        int nfds = epoll_wait(epoll_fd, events, sizeof(events) / sizeof(events[0]), -1);
        if (nfds < 0)
        {
            perror("epoll_wait");
            return false;
        }
        // [注意!] 此处必须是循环到 nfds, 不能多循环
        for (int i = 0; i < nfds; ++i)
        {
            TcpSocket sock(events[i].data.fd);
            output->push_back(sock);
        }
        return true;
    }
};

class TcpEpollServer
{
public:
    TcpEpollServer(const std::string &ip, uint16_t port) : ip_(ip), port_(port)
    {
    }
    bool Start(Handler handler)
    {
        Epoll epoll;
        // 1. 创建 socket
        TcpSocket listen_sock;
        assert(listen_sock.Socket());
        // 2. 绑定
        assert(listen_sock.Bind(ip_, port_));
        // 3. 监听
        assert(listen_sock.Listen(5));
        // 4. 创建 Epoll 对象, 并将 listen_sock 加入进去
        epoll.Add(listen_sock);
        // 5. 进入事件循环
        for (;;)
        {
            // 6. 进行 epoll_wait
            std::vector<TcpSocket> output;
            if (!epoll.Wait(&output))
            {
                continue;
            }
            // 7. 根据就绪的文件描述符的种类决定如何处理
            for (size_t i = 0; i < output.size(); ++i)
            {
                if (output[i].GetFd() == listen_sock.GetFd())
                {
                    // 如果是 listen_sock, 就调用 accept
                    TcpSocket new_sock;
                    listen_sock.Accept(&new_sock);
                    epoll.Add(new_sock);
                }
                else
                {
                    // 如果是 new_sock, 就进行一次读写
                    std::string req, resp;
                    bool ret = output[i].Recv(&req);
                    if (!ret)
                    {
                        // [注意!!] 需要把不用的 socket 关闭
                        // 先后顺序别搞反. 不过在 epoll 删除的时候其实就已经关闭 socket 了
                        epoll.Del(output[i]);
                        output[i].Close();
                        continue;
                    }
                    handler(req, &resp);
                    output[i].Send(resp);
                } // end for
            } // end for (;;)
        }
        return true;
    }

private:
    std::string ip_;
    uint16_t port_;
};